package com.itlaobing.aop.concepts;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * AOP 核心概念
 * 1、代理目标（proxy target）：哪个对象将被别的对象所代理
 * 2、代理对象（proxy object）：代理别的对象
 * 3、执行点（execution point）：任意可以执行的方法都可以成为一个执行点
 * 4、方位
 * 5、连接点（join point） 执行点 + 方位
 * 6、Advice 在任意连接点都可以添加需要 ”植入“ 的代码，被 ”植入“的代码被称作 Advice
 * 7、切点（pointcut）对 连接点 进行筛选的条件就是 切点
 * 8、切面（Aspect） = pointcut + Advice 即在哪里 加什么
 * 9、织入（Weaving） 将Advice 添加到 Pointcut 选择的连接点的过程
 *      织入方式：编译期织入 、类加载器织入 、运行期织入
 */

public class CalculatableTest3 {

    public static void main(String[] args) throws Exception {

        //代理目标（proxy target）
        final Calculatable target = new Calculator();

        Class<?> tc = target.getClass();
        ClassLoader loader = tc.getClassLoader();
        Class<?>[] interfaces = tc.getInterfaces();

        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                String methodName = method.getName();
                try {
                    // 方位：方法执行前（before）
                    if ("divide".equals(methodName)) {//条件：用来筛选 执行点 和 方位
                        System.out.println("方法[" + methodName + "]即将执行");// 我们需要添加的代码被称作 Advice
                    }
                    result = method.invoke(target, args); // 执行点 ( execution point )
                    // 方位: 方法返回后 ( after-return )
                    if( "divide".equals( methodName) ) { // 条件: 用来筛选 执行点 和 方位
                        System.err.println("方法[" + methodName + "]返回了[" + result + "]"); // 我们需要添加的代码被称作 Advice
                    }
                } catch ( Exception ex ) {
                    // 方位: 方法抛出异常后 ( after-throw )
                    if( "divide".equals( methodName) ) { // 条件: 用来筛选 执行点 和 方位
                        System.err.println("方法[" + methodName + "]抛出异常[" + ex.toString() + "]"); // 我们需要添加的代码被称作 Advice
                    }
                    ex.printStackTrace();
                } finally {
                    // 方位: 方法执行后 ( after )
                    if( "divide".equals( methodName) ) { // 条件: 用来筛选 执行点 和 方位
                        System.err.println("方法[" + methodName + "]执行结束"); // 我们需要添加的代码被称作 Advice
                    }
                }
                return result;
            }
        };
        Object proxy = Proxy.newProxyInstance(loader ,interfaces ,handler);

        String desc = proxy.toString();
        System.out.println(desc);

        if (proxy instanceof Calculatable){
            Calculatable c = (Calculatable) proxy;
            long result = c.divide(15,3);
            System.out.println(result);
        }
    }
}
